home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume22 / nn6.4 / part11 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  54.3 KB

  1. Subject:  v22i046:  NN Newsreader, release 6.4, Part11/21
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: bdd7a059 3aee2592 7e0d893e 7da71f7b
  5.  
  6. Submitted-by: "Kim F. Storm" <storm@texas.dk>
  7. Posting-number: Volume 22, Issue 46
  8. Archive-name: nn6.4/part11
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  init.c kill.c sort.c
  17. # Wrapped by storm@texas.dk on Sun May  6 18:19:45 1990
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 11 (of 22)."'
  21. if test -f 'init.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'init.c'\"
  23. else
  24.   echo shar: Extracting \"'init.c'\" \(21451 characters\)
  25.   sed "s/^X//" >'init.c' <<'END_OF_FILE'
  26. X/*
  27. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  28. X *
  29. X *    .nn/init file handling
  30. X */
  31. X
  32. X
  33. X#include "config.h"
  34. X#include "articles.h"
  35. X#include "term.h"
  36. X#include "keymap.h"
  37. X#include "menu.h"
  38. X
  39. Ximport char *help_directory, *db_directory;
  40. X
  41. Xexport int in_init = 0;        /* true when parsing init file */
  42. Xexport int alt_cmd_key;        /* K_ when parse_command returns AC_KEYCMD */
  43. X
  44. Xstatic int init_err = 0;    /* errors in init file */
  45. X
  46. X
  47. X/*VARARGS*/
  48. Xinit_message(va_alist)
  49. Xva_dcl
  50. X{
  51. X    char *fmt;
  52. X    use_vararg;
  53. X
  54. X    start_vararg;
  55. X
  56. X    if (in_init) {
  57. X    fmt = va_arg1(char *);
  58. X
  59. X    printf("init error: ");
  60. X    vprintf(fmt, va_args2toN);
  61. X    putchar(NL);
  62. X    init_err++;
  63. X    } else
  64. X    vmsg(va_args1toN);
  65. X
  66. X    end_vararg;
  67. X}
  68. X
  69. X
  70. X#define MAXARG 10
  71. X
  72. Xstatic char *argvec[MAXARG + 2];
  73. Xstatic int argc;
  74. X
  75. Xstatic char *strip_str(cmd)
  76. Xregister char *cmd;
  77. X{
  78. X    if (cmd == NULL) return cmd;
  79. X
  80. X    while (*cmd && isspace(*cmd)) cmd++;
  81. X    if (*cmd == NUL || *cmd == NL) return NULL;
  82. X
  83. X    return cmd;
  84. X}
  85. X
  86. X
  87. Xstatic split_command(cmd)
  88. Xregister char *cmd;
  89. X{
  90. X    /* split command string */
  91. X
  92. X    for (argc = 0; argc < MAXARG + 2; argc++) argvec[argc] = NULL;
  93. Xstrip_more:
  94. X    if ((cmd = strip_str(cmd)) == NULL || *cmd == '#') return 0;
  95. X    if (*cmd == ':') {
  96. X    cmd++;
  97. X    goto strip_more;
  98. X    }
  99. X
  100. X    argc = 0;
  101. X    argvec[0] = cmd;
  102. X
  103. X    if (in_init)
  104. X    while (*cmd) {
  105. X        if (*cmd == NL) {
  106. X        *cmd = NUL;
  107. X        break;
  108. X        }
  109. X        cmd++;
  110. X    }
  111. X
  112. X    return 1;
  113. X}
  114. X
  115. Xstatic char *argv(i)
  116. Xint i;
  117. X{
  118. X    register char *cmd;
  119. X
  120. X    if (i > MAXARG) return NULL;
  121. X
  122. X    if (argc <= i)
  123. X    if (cmd = argvec[argc])
  124. X        while (argc <= i) {
  125. X        while (*cmd && !isspace(*cmd)) cmd++;
  126. X        if (*cmd == NUL) {
  127. X            argc = MAXARG;
  128. X            break;
  129. X        }
  130. X
  131. X        *cmd++ = NUL;
  132. X        if ((cmd = strip_str(cmd)) == NULL) {
  133. X            argc = MAXARG;
  134. X            break;
  135. X        }
  136. X        argvec[++argc] = cmd;
  137. X        }
  138. X    else
  139. X        argc = MAXARG;
  140. X
  141. X    return argvec[i];
  142. X}
  143. X
  144. Xstatic is_sequence(cmd)
  145. Xchar *cmd;
  146. X{
  147. X    if (!split_command(cmd)) return 0;
  148. X    if ((cmd = argv(0)) == NULL) return 0;
  149. X    return strcmp(cmd, "sequence") == 0;
  150. X}
  151. X
  152. X#define START_SEQUENCE 555
  153. X
  154. Xstatic load_init_file(name, seq_hook_ptr, only_seq)
  155. Xchar *name;
  156. XFILE **seq_hook_ptr;
  157. X{
  158. X    FILE *init;
  159. X    char cmdbuf[512], *cmd, *term;
  160. X    extern char *term_name;
  161. X
  162. X    /* use cmdbuf temporarily (to handle @ expansion) */
  163. X    for (cmd = cmdbuf; *name; name++)
  164. X    if (*name == '@') {
  165. X        term = term_name;
  166. X        while (term && *term) *cmd++ = *term++;
  167. X    } else
  168. X        *cmd++ = *name;
  169. X    *cmd = NUL;
  170. X    name = cmdbuf;
  171. X
  172. X    if (strchr(name, '/') == NULL)
  173. X    name = relative(nn_directory, name);
  174. X
  175. X    init = open_file(name, OPEN_READ);
  176. X    if (init == NULL) return;
  177. X
  178. X    while (fgets(cmdbuf, 512, init)) {
  179. X    if (only_seq) {
  180. X        if (!is_sequence(cmdbuf)) continue;
  181. X        *seq_hook_ptr = init;
  182. X        return;
  183. X    }
  184. X    /* we use AC_REDRAW to avoid !-commands clear the screen */
  185. X    if (parse_command(cmdbuf, AC_REDRAW, init) == START_SEQUENCE) {
  186. X        if (seq_hook_ptr) {
  187. X        *seq_hook_ptr = init;
  188. X        return;    /* no close !! */
  189. X        } else {
  190. X        init_message("load file contains 'sequence'");
  191. X        fclose(init);
  192. X        return;
  193. X        }
  194. X    }
  195. X    }
  196. X
  197. X    fclose(init);
  198. X}
  199. X
  200. Xvisit_init_file(only_seq, first_arg)
  201. Xint only_seq;
  202. Xchar *first_arg;
  203. X{
  204. X    extern FILE *loc_seq_hook, *glob_seq_hook;
  205. X    char *next_arg;
  206. X
  207. X    if (first_arg && strncmp(first_arg, "-I", 2) == 0) {
  208. X    if (first_arg[2] == NUL) return;
  209. X    first_arg += 2;
  210. X    } else
  211. X    first_arg = ",init";
  212. X
  213. X    in_init = 1;
  214. X    while (first_arg) {
  215. X    next_arg = strchr(first_arg, ',');
  216. X    if (next_arg) *next_arg++ = NUL;
  217. X
  218. X    if (*first_arg == NUL) {
  219. X        if (glob_seq_hook == NULL)
  220. X        load_init_file(relative(lib_directory, "init"), &glob_seq_hook, only_seq);
  221. X    } else {
  222. X        if (loc_seq_hook != NULL) {
  223. X        fclose(loc_seq_hook);
  224. X        loc_seq_hook = NULL;
  225. X        }
  226. X        load_init_file(first_arg, &loc_seq_hook, only_seq);
  227. X    }
  228. X    first_arg = next_arg;
  229. X    }
  230. X
  231. X    if (init_err) nn_exit(1);
  232. X    in_init = 0;
  233. X}
  234. X
  235. X
  236. X/*
  237. X * parse a command (also :-commands)
  238. X */
  239. X
  240. Xstatic char *sw_string;
  241. X
  242. X#define    SWITCH(str)    \
  243. X    for (sw_string = str; sw_string; sw_string = NULL)
  244. X
  245. X#define CASE(str)    \
  246. X    if (strcmp(sw_string, str) == 0)
  247. X
  248. X
  249. X#define ARG(i, str)    (argv(i) && strcmp(argv(i), str) == 0)
  250. X#define ARGVAL(i)    atol(argv(i))
  251. X#define ARGTAIL        argvec[argc]
  252. X
  253. Xstruct alt_commands {
  254. X    char *alt_name;
  255. X    int     alt_len;
  256. X    int  alt_type;
  257. X} alt_commands[] = {
  258. X    "admin",            5,    0,
  259. X    "bug",            3,    0,
  260. X    "cd",            2,    1,
  261. X    "compile",            7,    0,
  262. X    "coredump",            8,    0,
  263. X    "cost",            4,    0,
  264. X    "decode",            6,    0,
  265. X    "define",            6,    0,
  266. X    "help",            4,    2,
  267. X    "local",            5,    3,
  268. X    "man",            3,    0,
  269. X    "map",            3,    -1,
  270. X    "map both",            8,    4,
  271. X    "map key",            7,    0,
  272. X    "map menu",            8,    4,
  273. X    "map show",            8,    4,
  274. X    "mkdir",            5,    1,
  275. X    "patch",            5,    0, /* QUICK HACK */
  276. X    "post",            4,    0, /* QUICK HACK */
  277. X    "print",            5,    0, /* QUICK HACK */
  278. X    "pwd",            3,    0,
  279. X    "rmail",            5,    0,
  280. X    "set",            3,    3,
  281. X    "show",            4,    -1,
  282. X    "show groups",        11,    -1,
  283. X    "show groups all",        15,    0,
  284. X    "show groups subscr",    18,    0,
  285. X    "show groups total",    17,    0,
  286. X    "show groups unsub",    17,    0,
  287. X    "show kill",        9,    0,
  288. X    "show map",            8,    -1,
  289. X    "show map #",        10,    0,
  290. X    "show map key",        12,    0,
  291. X    "show map menu",        13,    0,
  292. X    "show map show",        13,    0,
  293. X    "show rc ",            8,    0,
  294. X    "sort",            4,    -1,
  295. X    "sort arrival",        12,    0,
  296. X    "sort date",        9,    0,
  297. X    "sort lexical",        12,    0,
  298. X    "sort sender",        11,    0,
  299. X    "sort subject",        12,    0,
  300. X    "toggle",            6,    3,
  301. X    "unread",            6,    0,
  302. X    "unset",            5,    3,
  303. X    "unshar",            6,    0, /* QUICK HACK */
  304. X    NULL,            0,    0
  305. X};
  306. X
  307. Xalt_completion(buf, index)
  308. Xchar *buf;
  309. Xint index;
  310. X{
  311. X    static char *head, *tail = NULL, buffer[FILENAME];
  312. X    static int len;
  313. X    static struct alt_commands *alt, *help_alt;
  314. X    static fct_type other_compl;
  315. X    int temp;
  316. X    register char *p, *q;
  317. X    extern int file_completion(), var_completion(), cmd_completion();
  318. X    extern int list_offset;
  319. X
  320. X    if (other_compl) {
  321. X    temp = CALL(other_compl)(buf, index);
  322. X    if (index == 0 && temp == 1 && tail) strcpy(tail, head);
  323. X    if (index < 0 || (index == 0 && temp == 0)) {
  324. X        other_compl = NULL;
  325. X        list_offset = 0;
  326. X    }
  327. X    return temp;
  328. X    }
  329. X
  330. X    if (index < 0) return 0;
  331. X
  332. X    if (buf) {
  333. X    head = buf;
  334. X    tail = buf + index;
  335. X    alt = help_alt = alt_commands;
  336. X    len = tail - head;
  337. X    other_compl = NULL;
  338. X
  339. X    for (; alt->alt_name; alt++) {
  340. X        if (len <= alt->alt_len || head[alt->alt_len] != SP) continue;
  341. X        index = strncmp(alt->alt_name, head, alt->alt_len);
  342. X        if (index < 0) continue;
  343. X        if (index > 0) break;
  344. X
  345. X        if (alt->alt_type < 0) {
  346. X        if (len > alt->alt_len) continue;
  347. X        break;
  348. X        }
  349. X
  350. X        if (alt->alt_type == 0) return -1; /* cannot be further compl */
  351. X
  352. X        head += alt->alt_len;
  353. X        while (*head && *head == SP) head++;
  354. X        len = tail - head;
  355. X        temp = -1;
  356. X
  357. X        switch (alt->alt_type) {
  358. X         case 1:
  359. X        other_compl = file_completion;
  360. X        tail = NULL;
  361. X        temp = file_completion(head, len);
  362. X        break;
  363. X
  364. X         case 2:
  365. X        other_compl = file_completion;
  366. X        sprintf(buffer, "%s.%s",
  367. X            relative(help_directory, "help"), head);
  368. X        len = strlen(buffer);
  369. X        head = buffer + len;
  370. X        list_offset = 5;
  371. X        temp = file_completion(buffer, len);
  372. X        break;
  373. X
  374. X         case 3:
  375. X        /* [set ]variable[ value] */
  376. X        for (p = head; *p; )
  377. X            if (*p++ == SP) return -1;
  378. X        other_compl = var_completion;
  379. X        tail = NULL;
  380. X        temp = var_completion(head, len);
  381. X        break;
  382. X
  383. X         case 4:
  384. X        /* [map XXX ]Y command[ N] */
  385. X        for (p = head, temp = 0; *p; )
  386. X            if (*p++ == SP) {
  387. X            while (*p && *p == SP) p++;
  388. X            head = p;
  389. X            temp++;
  390. X            }
  391. X        if (temp != 1) return -1;
  392. X
  393. X        other_compl = cmd_completion;
  394. X        tail = NULL;
  395. X        len = p - head;
  396. X        temp = cmd_completion(head, len);
  397. X        break;
  398. X        }
  399. X        if (temp <= 0) other_compl = NULL;
  400. X        return temp;
  401. X    }
  402. X
  403. X    alt = alt_commands;
  404. X    return 1;
  405. X    }
  406. X
  407. X    if (index) {
  408. X    list_completion((char *)NULL);
  409. X    if (help_alt->alt_name == NULL) help_alt = alt_commands;
  410. X    list_offset = 0;
  411. X    if (p = strrchr(head, ' ')) list_offset = p - head;
  412. X
  413. X    while (help_alt->alt_name) {
  414. X        if (len > help_alt->alt_len ||
  415. X        (index = strncmp(help_alt->alt_name, head, len)) < 0) {
  416. X        help_alt++;
  417. X        continue;
  418. X        }
  419. X        if (index > 0) {
  420. X        help_alt = alt_commands;
  421. X        break;
  422. X        }
  423. X        p = help_alt->alt_name;
  424. X        if (list_completion(p) == 0) break;
  425. X        temp = help_alt->alt_len;
  426. X
  427. X        do help_alt++;
  428. X        while ((q = help_alt->alt_name) && help_alt->alt_len > temp &&
  429. X           strncmp(p, q, temp) == 0);
  430. X    }
  431. X    fl;
  432. X    list_offset = 0;
  433. X    return 1;
  434. X    }
  435. X
  436. X    for (; alt->alt_name; alt++) {
  437. X    if (len == 0)
  438. X        index = 0;
  439. X    else
  440. X        index = strncmp(alt->alt_name, head, len);
  441. X    if (index < 0) continue;
  442. X    if (index > 0) break;
  443. X
  444. X    p = alt->alt_name;
  445. X    sprintf(tail, "%s ", p + len);
  446. X    temp = alt->alt_len;
  447. X
  448. X    do alt++;
  449. X    while ((q = alt->alt_name) && alt->alt_len > temp &&
  450. X           strncmp(p, q, temp) == 0);
  451. X
  452. X    return 1;
  453. X    }
  454. X    return 0;
  455. X}
  456. X
  457. Xstatic print_debug_info()
  458. X{
  459. X    clrdisp();
  460. X    printf("group=%s, nart=%ld\n\r", current_group->group_name, n_articles);
  461. X    any_key(0);
  462. X}
  463. X
  464. Xstatic print_command(str)
  465. Xchar *str;
  466. X{
  467. X    char **av;
  468. X
  469. X    if (!in_init) {
  470. X    msg(str);
  471. X    return;
  472. X    }
  473. X
  474. X    printf("\r%s:", str);
  475. X    for (av = argvec; *av; av++)
  476. X        printf(" %s", *av);
  477. X    putchar(NL);
  478. X}
  479. X
  480. X
  481. Xstatic do_show(table, mode_arg)
  482. Xchar *table;
  483. Xint mode_arg;
  484. X{
  485. X    register group_header *gh;
  486. X
  487. X    if (in_init || table == NULL) return 0;
  488. X
  489. X    no_raw();
  490. X
  491. X    SWITCH( table ) {
  492. X
  493. X    CASE( "kill" ) {
  494. X        clrdisp();
  495. X        dump_kill_list();
  496. X        break;
  497. X    }
  498. X
  499. X    CASE( "groups" ) {
  500. X
  501. X        clrdisp();
  502. X        if ARG(mode_arg, "all")
  503. X        group_overview(1);
  504. X        else
  505. X        if ARG(mode_arg, "total")
  506. X        group_overview(2);
  507. X        else
  508. X        if ARG(mode_arg, "unsub")
  509. X        group_overview(3);
  510. X        else
  511. X        group_overview(0);
  512. X
  513. X        break;
  514. X    }
  515. X
  516. X    CASE( "map" ) {
  517. X        char *name;
  518. X        extern in_menu_mode;
  519. X
  520. X        if ((name = argv(mode_arg)) == NULL)
  521. X        name = in_menu_mode ? "menu" : "show";
  522. X
  523. X        if (name[0] == '#') {
  524. X        clrdisp();
  525. X        dump_multi_keys();
  526. X        break;
  527. X        }
  528. X
  529. X        SWITCH( name ) {
  530. X
  531. X        CASE( "key" ) {
  532. X            clrdisp();
  533. X            dump_global_map();
  534. X            break;
  535. X        }
  536. X        CASE( "menu" ) {
  537. X            clrdisp();
  538. X            dump_key_map(menu_key_map, "menu", K_ONLY_MENU);
  539. X            break;
  540. X        }
  541. X        CASE( "show" ) {
  542. X            clrdisp();
  543. X            dump_key_map(more_key_map, "show", K_ONLY_MORE);
  544. X            break;
  545. X        }
  546. X
  547. X        init_message("unknown map '%s'", argv(mode_arg));
  548. X        goto err;
  549. X        /*NOTREACHED*/
  550. X        }
  551. X
  552. X        break;
  553. X    }
  554. X
  555. X    CASE( "rc" ) {
  556. X        if (argv(2)) {
  557. X        gh = lookup(argv(2));
  558. X        if (gh == NULL) {
  559. X            msg("Unknown: %s", argv(2));
  560. X            break;
  561. X        }
  562. X        } else
  563. X        gh = current_group;
  564. X        if (gh->group_flag & G_FAKED) break;
  565. X
  566. X        clrdisp();
  567. X
  568. X        printf("Available: %ld - %ld  (unread %ld)\n\n",
  569. X           (long)(gh->first_db_article), (long)(gh->last_db_article),
  570. X           (long)(gh->unread_count));
  571. X        printf(".newsrc:\n\r>%s\r<%s\n\rselect:\n\r>%s\r<%s\n\r",
  572. X           gh->newsrc_line ? gh->newsrc_line : "(null)\n",
  573. X           gh->newsrc_orig == gh->newsrc_line ? "(same)\n" :
  574. X           gh->newsrc_orig ? gh->newsrc_orig : "(null)\n",
  575. X           gh->select_line ? gh->select_line : "(null)\n",
  576. X           gh->select_orig == gh->select_line ? "(same)\n" :
  577. X           gh->select_orig ? gh->select_orig : "(null)\n");
  578. X        any_key(0);
  579. X        break;
  580. X    }
  581. X
  582. X    init_message("unknown table '%s'", table);
  583. X    goto err;
  584. X    /*NOTREACHED*/
  585. X    }
  586. X
  587. X    raw();
  588. X    return 1;
  589. Xerr:
  590. X    raw();
  591. X    return 0;
  592. X}
  593. X
  594. X
  595. Xstatic do_map(initf)
  596. XFILE *initf;
  597. X{
  598. X    int code, map_menu, map_show, must_redraw = 0;
  599. X
  600. X    SWITCH( argv(1) ) {
  601. X
  602. X    CASE( "key" ) {
  603. X        if (argv(3) == NULL) break;
  604. X        global_key_map[parse_key(argv(2))] = parse_key(argv(3));
  605. X        goto out;
  606. X    }
  607. X
  608. X    if (argv(1)[0] == '#') {
  609. X        key_type multi_buffer[16], *mb;
  610. X        int i;
  611. X
  612. X        if (!isdigit(argv(1)[1])) break;
  613. X
  614. X        for (i = 2, mb = multi_buffer; argv(i); i++)
  615. X        *mb++ = parse_key(argv(i));
  616. X        *mb = NUL;
  617. X
  618. X        enter_multi_key(K_function(argv(1)[1] - '0'),
  619. X                (key_type *)copy_str((char *)multi_buffer));
  620. X
  621. X        goto out;
  622. X    }
  623. X
  624. X    code = K_UNBOUND;
  625. X    map_menu = map_show = 0;
  626. X
  627. X    CASE( "menu" ) {
  628. X        map_menu++;
  629. X    }
  630. X    CASE( "show" ) {
  631. X        map_show++;
  632. X    }
  633. X    CASE( "both" ) {
  634. X        map_menu++;
  635. X        map_show++;
  636. X    }
  637. X
  638. X    if (ARG(3, "(")) {
  639. X        extern char *m_define();
  640. X
  641. X        code = (int)m_define("-2", initf);
  642. X        must_redraw = 1;
  643. X        if (code == K_UNBOUND) goto mac_err;
  644. X    }
  645. X
  646. X    if (map_menu) {
  647. X        if (code == K_UNBOUND && argv(3))
  648. X        code = lookup_command(argv(3), K_ONLY_MENU);
  649. X
  650. X        if (code == K_EQUAL_KEY) {
  651. X        if (argv(4))
  652. X            code = menu_key_map[parse_key(argv(4))];
  653. X        else
  654. X            goto mac_err;
  655. X        } else
  656. X        if (code == K_MACRO || code == K_ARTICLE_ID)
  657. X        if (argv(4))
  658. X            code |= atoi(argv(4));
  659. X        else
  660. X            goto mac_err;
  661. X
  662. X        if (code != K_INVALID) {
  663. X        menu_key_map[parse_key(argv(2))] = code;
  664. X        if (!map_show) goto out;
  665. X        }
  666. X    }
  667. X
  668. X    if (map_show) {
  669. X        if (code == K_UNBOUND && argv(3))
  670. X        code = lookup_command(argv(3), K_ONLY_MORE);
  671. X
  672. X        if (code == K_EQUAL_KEY) {
  673. X        if (argv(4))
  674. X            code = menu_key_map[parse_key(argv(4))];
  675. X        else
  676. X            goto mac_err;
  677. X        } else
  678. X        if (code == K_MACRO)
  679. X        if (argv(4))
  680. X            code |= atoi(argv(4));
  681. X        else
  682. X            goto mac_err;
  683. X
  684. X        if (code != K_INVALID) {
  685. X        more_key_map[parse_key(argv(2))] = code;
  686. X        goto out;
  687. X        }
  688. X    }
  689. X
  690. X    if (argv(4)) break;
  691. X
  692. X    if (code == K_INVALID) {
  693. X        init_message("unknown key command: %s", argv(3));
  694. X        goto out;
  695. X    }
  696. X    }
  697. X
  698. X    print_command("syntax error");
  699. X    goto out;
  700. X
  701. X mac_err:
  702. X    print_command("map argument missing");
  703. X out:
  704. X    return must_redraw;
  705. X}
  706. X
  707. Xstatic parse_on_to_end(f)
  708. XFILE *f;
  709. X{
  710. X    register char *cp;
  711. X    char buf[256];
  712. X
  713. X    if (argv(1) == NULL) goto on_err;
  714. X
  715. X    SWITCH ( argv(1) ) {
  716. X
  717. X    CASE( "entry" ) {
  718. X        import char *dflt_enter_macro;
  719. X        group_header *gh, *get_group_search();
  720. X        char *macro, *parse_enter_macro();
  721. X        int i;
  722. X
  723. X        macro = parse_enter_macro(f, NL);
  724. X        if (ARGTAIL) {
  725. X        for (i = 2; argv(i); i++) {
  726. X            start_group_search(argv(i));
  727. X            while (gh = get_group_search())
  728. X            gh->enter_macro = macro;
  729. X        }
  730. X        } else
  731. X        dflt_enter_macro = macro;
  732. X        return;
  733. X    }
  734. X
  735. X/*    CASE( "exit" ) {
  736. X        import char *dflt_exit_macro;
  737. X
  738. X        dflt_exit_macro = parse_enter_macro(f, NL);
  739. X        return;
  740. X    }
  741. X*/
  742. X    CASE( "slow" ) {
  743. X        import int terminal_speed, slow_speed;
  744. X
  745. X        if (terminal_speed <= (slow_speed / 10)) return;
  746. X        break;
  747. X    }
  748. X
  749. X    CASE( "fast" ) {
  750. X        import int terminal_speed, slow_speed;
  751. X
  752. X        if (terminal_speed > (slow_speed / 10)) return;
  753. X        break;
  754. X    }
  755. X
  756. X    CASE( "term" ) {
  757. X        extern char *term_name;
  758. X        int i;
  759. X
  760. X        for (i = 2; argv(i) != NULL; i++)
  761. X        if (strcmp(argv(2), term_name) == 0) return;
  762. X        break;
  763. X    }
  764. X
  765. X    CASE( "host" ) {
  766. X        char local_host[100];
  767. X        int i;
  768. X
  769. X        gethostname(local_host, 100);
  770. X        for (i = 2; argv(i) != NULL; i++)
  771. X        if (strcmp(argv(2), local_host) == 0) return;
  772. X        break;
  773. X    }
  774. X
  775. X    goto on_err;
  776. X    }
  777. X
  778. X    while (fgets(buf, 256, f) != NULL) {
  779. X    for (cp = buf; *cp && isascii(*cp) && isspace(*cp); cp++);
  780. X    if (strncmp(cp, "end", 3) == 0) return;
  781. X    }
  782. X    init_message("end missing (on %s)", argv(1));
  783. X    return;
  784. X    
  785. Xon_err:
  786. X    init_message("on `what'?");
  787. X}
  788. X
  789. Xparse_command(cmd, ok_val, initf)
  790. Xchar *cmd;
  791. Xint ok_val;
  792. XFILE *initf;
  793. X{
  794. X    extern char *m_define(), *parse_enter_macro();
  795. X
  796. X    if (!split_command(cmd)) return ok_val;
  797. X
  798. X    if (*ARGTAIL == '!') {
  799. X    if (ok_val == AC_UNCHANGED) { /* in macro */
  800. X        if (ARGTAIL[1] == '!') /* !!cmd => guarantee no output! */
  801. X        run_shell(ARGTAIL+2, -2);
  802. X        else
  803. X        run_shell(ARGTAIL+1, -1);
  804. X        return ok_val;
  805. X    }
  806. X    if (run_shell(ARGTAIL+1, ok_val == AC_PROMPT ? 1 : 0)) {
  807. X        any_key(0);
  808. X        return AC_REDRAW;
  809. X    }
  810. X    return ok_val;
  811. X    }
  812. X
  813. X    SWITCH( argv(0) ) {
  814. X
  815. X    CASE( "unset" ) {
  816. X        if (argv(1) == NULL) goto stx_err;
  817. X
  818. X        if (set_variable(argv(1), 0, (char *)NULL))
  819. X        return AC_REDRAW;
  820. X        else
  821. X        return ok_val;
  822. X    }
  823. X
  824. X    CASE( "local" ) {
  825. X        if (ARGTAIL == NULL) goto stx_err;
  826. X
  827. X        cmd = argv(1);
  828. X        if (!push_variable(cmd)) return ok_val;
  829. X
  830. X        if (ARGTAIL && set_variable(cmd, 1, ARGTAIL))
  831. X        return AC_REDRAW;
  832. X        else
  833. X        return ok_val;
  834. X    }
  835. X
  836. X    CASE( "set" ) {
  837. X        if (ARGTAIL == NULL) {
  838. X        disp_variables(0);
  839. X        return AC_REDRAW;
  840. X        }
  841. X
  842. X        cmd = argv(1);    /* get ARGTAIL right */
  843. X        if (cmd != NULL && strcmp(cmd, "all") == 0) {
  844. X        disp_variables(1);
  845. X        return AC_REDRAW;
  846. X        }
  847. X
  848. X        if (set_variable(cmd, 1, ARGTAIL))
  849. X        return AC_REDRAW;
  850. X        else
  851. X        return ok_val;
  852. X    }
  853. X
  854. X    CASE( "toggle" ) {
  855. X        if (argv(1) == NULL) goto stx_err;
  856. X        toggle_variable(argv(1));
  857. X        break;
  858. X    }
  859. X
  860. X    CASE( "define" ) {
  861. X        if (in_init) {
  862. X        if (argv(1) == NULL) {
  863. X            init_message("macro number missing");
  864. X            break;
  865. X        }
  866. X        m_define(argv(1), initf);
  867. X        } else
  868. X        if (m_define(argv(1), (FILE *)NULL))
  869. X            return AC_REDRAW;
  870. X
  871. X        break;
  872. X    }
  873. X
  874. X    CASE( "map" ) {
  875. X        if (argv(2) == NULL) {
  876. X        if (do_show("map", 1))
  877. X            return AC_REDRAW;
  878. X        break;
  879. X        }
  880. X
  881. X        if (do_map(initf))
  882. X        return AC_REDRAW;
  883. X        break;
  884. X    }
  885. X
  886. X    CASE( "cd" ) {
  887. X        if (change_dir(argv(1), in_init))
  888. X        init_message("chdir %s FAILED", argv(1));
  889. X
  890. X        break;
  891. X    }
  892. X
  893. X    if (in_init) {
  894. X
  895. X        CASE( "load" ) {
  896. X        if (argv(1)) load_init_file(argv(1), (FILE **)NULL, 0);
  897. X        break;
  898. X        }
  899. X
  900. X        CASE( "on" ) {
  901. X        parse_on_to_end(initf);
  902. X        break;
  903. X        }
  904. X
  905. X        CASE( "end" ) {
  906. X        break;
  907. X        }
  908. X
  909. X        CASE( "sequence" ) {
  910. X        return START_SEQUENCE;
  911. X        }
  912. X
  913. X        CASE( "save-files" ) {
  914. X        parse_save_files(initf);
  915. X        break;
  916. X        }
  917. X
  918. X        print_command("unknown command");
  919. X        break;
  920. X    }
  921. X
  922. X    /*
  923. X     * commands only available from : command line
  924. X     */
  925. X
  926. X    if (ok_val != AC_REDRAW) {
  927. X        extern in_menu_mode;
  928. X
  929. X        alt_cmd_key = lookup_command(sw_string,
  930. X                 in_menu_mode ? K_ONLY_MENU : K_ONLY_MORE);
  931. X        if (alt_cmd_key != K_INVALID && alt_cmd_key != K_HELP)
  932. X        return AC_KEYCMD;
  933. X    }
  934. X
  935. X    CASE( "q" ) {
  936. X        break;
  937. X    }
  938. X
  939. X    CASE( "Q" ) {
  940. X        return AC_QUIT;
  941. X    }
  942. X
  943. X    CASE( "q!" ) {
  944. X        if (restore_bak())
  945. X        return AC_QUIT;
  946. X        break;
  947. X    }
  948. X
  949. X    CASE( "x" ) {
  950. X        update_rc_all(current_group, 0);
  951. X        return AC_QUIT;
  952. X    }
  953. X
  954. X    CASE( "help" ) {
  955. X        if (argv(1) == NULL)
  956. X        display_help("help");
  957. X        else
  958. X        display_help(argv(1));
  959. X        return AC_REDRAW;
  960. X    }
  961. X
  962. X    CASE( "man" ) {
  963. X        char *manual;
  964. X        group_header *orig_group;
  965. X        int orig_layout, orig_fsort;
  966. X        import int fmt_linenum, dont_sort_folders;
  967. X
  968. X        manual = relative(help_directory, "Manual");
  969. X        if (!file_exist(manual, "fr")) {
  970. X        manual = relative(db_directory, "Manual");
  971. X        if (!file_exist(manual, "fr")) {
  972. X            msg("Online manual is not available");
  973. X            break;
  974. X        }
  975. X        }
  976. X        orig_group = current_group;
  977. X        orig_layout = fmt_linenum;
  978. X        orig_fsort = dont_sort_folders;
  979. X
  980. X        fmt_linenum = -1;
  981. X        dont_sort_folders = 1;
  982. X
  983. X        folder_menu(manual);
  984. X
  985. X        fmt_linenum = orig_layout;
  986. X        dont_sort_folders = orig_fsort;
  987. X        init_group(orig_group);
  988. X
  989. X        return AC_REDRAW;
  990. X    }
  991. X
  992. X    CASE( "sort" ) {
  993. X        if (argv(1) == NULL)
  994. X        sort_articles(-1);
  995. X        else if (ARG(1, "no") || ARG(1, "arrival"))
  996. X        sort_articles(0);
  997. X        else if ARG(1, "subject")
  998. X        sort_articles(1);
  999. X        else if ARG(1, "lexical")
  1000. X        sort_articles(2);
  1001. X        else if (ARG(1, "date") || ARG(1, "age"))
  1002. X        sort_articles(3);
  1003. X        else if (ARG(1, "sender") || ARG(1, "from"))
  1004. X        sort_articles(4);
  1005. X        else {
  1006. X        msg("Unknown sort mode '%s'", argv(1));
  1007. X        break;
  1008. X        }
  1009. X
  1010. X        return AC_REORDER;
  1011. X    }
  1012. X
  1013. X    CASE( "unread" ) {
  1014. X        group_header *gh;
  1015. X        int ix;
  1016. X        int32 restore_rc();
  1017. X
  1018. X        if (argv(1) && (gh = lookup(argv(1))) != NULL)
  1019. X        ix = 2;
  1020. X        else {
  1021. X        ix = 1;
  1022. X        gh = current_group;
  1023. X        }
  1024. X
  1025. X        if (gh == current_group) return AC_REENTER_GROUP;
  1026. X
  1027. X        if (argv(ix)) {
  1028. X        if (!restore_rc(gh, gh->last_db_article - ARGVAL(ix)))
  1029. X            break;
  1030. X        } else
  1031. X        if (!restore_unread(gh))
  1032. X            break;
  1033. X        break;
  1034. X    }
  1035. X
  1036. X    CASE( "dump" ) {
  1037. X        if (do_show(argv(1), 2))
  1038. X        return AC_REDRAW;
  1039. X        break;
  1040. X    }
  1041. X
  1042. X    CASE( "show" ) {
  1043. X        if (do_show(argv(1), 2))
  1044. X        return AC_REDRAW;
  1045. X        break;
  1046. X    }
  1047. X
  1048. X    CASE( "compile" ) {
  1049. X        import int do_kill_handling;
  1050. X
  1051. X        clrdisp();
  1052. X        rm_kill_file();
  1053. X        free_kill_entries();
  1054. X        do_kill_handling = init_kill() && do_kill_handling;
  1055. X        return AC_REDRAW;
  1056. X    }
  1057. X
  1058. X    CASE( "pwd" ) {
  1059. X        FILE *p = popen("exec pwd", "r");
  1060. X        char dir[FILENAME];
  1061. X        if (p) {
  1062. X        if (fgets(dir, FILENAME, p)) {
  1063. X            dir[strlen(dir) - 1] = NUL;
  1064. X            msg("%s", dir);
  1065. X        }
  1066. X        pclose(p);
  1067. X        }
  1068. X        break;
  1069. X    }
  1070. X
  1071. X    CASE( "rmail" ) {
  1072. X        import char *mail_box;
  1073. X        group_header *orig_group;
  1074. X
  1075. X        if (mail_box == NULL) {
  1076. X        msg("'mail' path not defined");
  1077. X        break;
  1078. X        }
  1079. X
  1080. X        orig_group = current_group;
  1081. X        folder_menu(mail_box);
  1082. X        init_group(orig_group);
  1083. X
  1084. X        return AC_REDRAW;
  1085. X    }
  1086. X
  1087. X    CASE( "mkdir" ) {
  1088. X        char *dir, *run_mkdir();
  1089. X        char name_buf[FILENAME];
  1090. X
  1091. X        if (dir = run_mkdir(argv(1), name_buf)) {
  1092. X        prompt("Change to %s", dir);
  1093. X        if (yes(0)) change_dir(dir, 0);
  1094. X        }
  1095. X        break;
  1096. X    }
  1097. X
  1098. X    CASE( "sh" ) {
  1099. X        suspend_nn();
  1100. X        s_redraw = 0;
  1101. X        return AC_REDRAW;
  1102. X    }
  1103. X
  1104. X    CASE( "admin" ) {
  1105. X        group_header *cur_group;
  1106. X
  1107. X        cur_group = current_group;
  1108. X        no_raw();
  1109. X        clrdisp();
  1110. X        printf("\n\n\n\rADMINISTRATION MODE\r\n\n\n");
  1111. X        admin_mode((char *)NULL);
  1112. X        clrdisp();
  1113. X        raw();
  1114. X        init_group(cur_group);
  1115. X        return AC_REDRAW;
  1116. X    }
  1117. X
  1118. X    CASE( "cost" ) {
  1119. X#ifdef ACCOUNTING
  1120. X        gotoxy(0, Lines-1); clrline();
  1121. X        account('C', 1);
  1122. X#else
  1123. X        msg("No accounting");
  1124. X#endif
  1125. X        break;
  1126. X    }
  1127. X
  1128. X    CASE( "bug" ) {
  1129. X        if (answer((article_header *)NULL, K_BUG_REPORT, 0))
  1130. X        return AC_REDRAW;
  1131. X        break;
  1132. X    }
  1133. X
  1134. X    CASE( "debug" ) {
  1135. X        print_debug_info();
  1136. X        return AC_REDRAW;
  1137. X    }
  1138. X
  1139. X    CASE( "coredump" ) {
  1140. X        unset_raw();
  1141. X        abort();
  1142. X    }
  1143. X
  1144. X    msg("unknown command: \"%s\"", argv(0));
  1145. X     }
  1146. X
  1147. X    return ok_val;
  1148. X
  1149. X stx_err:
  1150. X    print_command("syntax error");
  1151. X    return ok_val;
  1152. X}
  1153. X
  1154. X
  1155. Xdisplay_help(subject)
  1156. Xchar *subject;
  1157. X{
  1158. X    char file[FILENAME];
  1159. X
  1160. X    strcpy(file, "help.");
  1161. X    strcpy(file+5, subject);
  1162. X
  1163. X    display_file(file, CLEAR_DISPLAY | CONFIRMATION);
  1164. X}
  1165. END_OF_FILE
  1166.   if test 21451 -ne `wc -c <'init.c'`; then
  1167.     echo shar: \"'init.c'\" unpacked with wrong size!
  1168.   fi
  1169.   # end of 'init.c'
  1170. fi
  1171. if test -f 'kill.c' -a "${1}" != "-c" ; then 
  1172.   echo shar: Will not clobber existing file \"'kill.c'\"
  1173. else
  1174.   echo shar: Extracting \"'kill.c'\" \(21836 characters\)
  1175.   sed "s/^X//" >'kill.c' <<'END_OF_FILE'
  1176. X/*
  1177. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  1178. X *
  1179. X *    Kill file handling
  1180. X */
  1181. X
  1182. X#include "config.h"
  1183. X#include "term.h"
  1184. X#include "regexp.h"
  1185. X
  1186. Xexport int killed_articles;
  1187. X
  1188. Xchar KILL_FILE[] =     "kill";
  1189. Xchar COMPILED_KILL[] =    "KILL.COMP";
  1190. X
  1191. X#define COMP_KILL_MAGIC    0x4b694c6f    /* KiLo */
  1192. X
  1193. X/*
  1194. X * kill flags
  1195. X */
  1196. X
  1197. X#define COMP_KILL_ENTRY        0x80000000
  1198. X
  1199. X#define GROUP_REGEXP        0x01000000
  1200. X#define GROUP_REGEXP_HDR    0x02000000
  1201. X
  1202. X#define AND_MATCH        0x00020000
  1203. X#define OR_MATCH        0x00010000
  1204. X
  1205. X#define    KILL_CASE_MATCH        0x00000100
  1206. X#define KILL_ON_REGEXP        0x00000200
  1207. X#define KILL_UNLESS_MATCH    0x00000400
  1208. X
  1209. X#define    AUTO_KILL        0x00000001
  1210. X#define AUTO_SELECT        0x00000002
  1211. X#define ON_SUBJECT        0x00000004
  1212. X#define    ON_SENDER        0x00000008
  1213. X#define ON_FOLLOW_UP        0x00000010
  1214. X#define ON_ANY_REFERENCES    0x00000020
  1215. X
  1216. X/*
  1217. X * external flag representation
  1218. X */
  1219. X
  1220. X#define    EXT_AUTO_KILL        '!'
  1221. X#define EXT_AUTO_SELECT        '+'
  1222. X#define EXT_KILL_UNLESS_MATCH    '~'
  1223. X#define EXT_ON_FOLLOW_UP    '>'
  1224. X#define EXT_ON_ANY_REFERENCES    'a'
  1225. X#define EXT_ON_SUBJECT        's'
  1226. X#define    EXT_ON_SENDER        'n'
  1227. X#define    EXT_KILL_CASE_MATCH    '='
  1228. X#define EXT_KILL_ON_REGEXP    '/'
  1229. X#define EXT_AND_MATCH        '&'
  1230. X#define EXT_OR_MATCH        '|'
  1231. X
  1232. X/*
  1233. X * period = nnn DAYS
  1234. X */
  1235. X
  1236. X#define    DAYS    * 24 * 60 * 60
  1237. X
  1238. X
  1239. X/*
  1240. X * kill_article
  1241. X *
  1242. X *    return 1 to kill article, 0 to include it
  1243. X */
  1244. X
  1245. Xtypedef struct kill_list_entry {
  1246. X    flag_type kill_flag;
  1247. X    char *kill_pattern;
  1248. X    regexp *kill_regexp;
  1249. X    struct kill_list_entry *next_kill;
  1250. X} kill_list_entry;
  1251. X
  1252. Xstatic kill_list_entry *kill_tab;
  1253. Xstatic char *kill_patterns;
  1254. X
  1255. Xstatic kill_list_entry *global_kill_list = NULL;
  1256. Xstatic kill_list_entry latest_kl_entry;
  1257. X
  1258. Xtypedef struct {
  1259. X    regexp *group_regexp;
  1260. X    kill_list_entry *kill_entry;
  1261. X} kill_group_regexp;
  1262. X
  1263. Xstatic kill_group_regexp *group_regexp_table = NULL;
  1264. Xstatic int regexp_table_size = 0;
  1265. Xstatic kill_list_entry *regexp_kill_list = NULL;
  1266. Xstatic group_header *current_kill_group = NULL;
  1267. X
  1268. X/*
  1269. X *    Build regexp_kill_list for current_group
  1270. X */
  1271. X
  1272. Xstatic build_regexp_kill()
  1273. X{
  1274. X    register kill_group_regexp *tb;
  1275. X    register int n, used_last;
  1276. X    register char *name;
  1277. X
  1278. X    regexp_kill_list = NULL;
  1279. X    current_kill_group = current_group;
  1280. X    name = current_group->group_name;
  1281. X    used_last = 0;        /* get AND_MATCH/OR_MATCH for free */
  1282. X
  1283. X    for (n = regexp_table_size, tb = group_regexp_table; --n >= 0; tb++) {
  1284. X    if (tb->group_regexp != NULL) {
  1285. X        used_last = 0;
  1286. X        if (!regexec(tb->group_regexp, name)) continue;
  1287. X    } else
  1288. X        if (!used_last) continue;
  1289. X
  1290. X    tb->kill_entry->next_kill = regexp_kill_list;
  1291. X    regexp_kill_list = tb->kill_entry;
  1292. X    used_last = 1;
  1293. X    }
  1294. X}
  1295. X
  1296. X/*
  1297. X *    execute kill patterns on article
  1298. X */
  1299. X
  1300. Xstatic kill_list_entry *exec_kill(kl, ah, unlessp, do_kill, do_select)
  1301. Xregister kill_list_entry *kl;
  1302. Xregister article_header *ah;
  1303. Xint *unlessp, do_kill, do_select;
  1304. X{
  1305. X    register flag_type flag;
  1306. X    register char *string;
  1307. X
  1308. X    for ( ; kl != NULL; kl = kl->next_kill) {
  1309. X    flag = kl->kill_flag;
  1310. X
  1311. X    if (do_select && (flag & AUTO_SELECT) == 0) goto failed;
  1312. X    if (do_kill && (flag & AUTO_KILL) == 0) goto failed;
  1313. X
  1314. X    if (flag & KILL_UNLESS_MATCH)
  1315. X        *unlessp = 1;
  1316. X
  1317. X    if (flag & ON_ANY_REFERENCES) {
  1318. X        if (ah->replies & 0x7f) goto match;
  1319. X        goto failed;
  1320. X    }
  1321. X
  1322. X    if (flag & ON_SUBJECT) {
  1323. X        if (flag & ON_FOLLOW_UP) {
  1324. X        if ((ah->replies & 0x80) == 0) goto failed;
  1325. X        }
  1326. X        string = ah->subject;
  1327. X    } else
  1328. X        string = ah->sender;
  1329. X
  1330. X    if (flag & KILL_CASE_MATCH) {
  1331. X        if (flag & KILL_ON_REGEXP) {
  1332. X        if (regexec(kl->kill_regexp, string)) goto match;
  1333. X        } else
  1334. X        if (strcmp(kl->kill_pattern, string) == 0) goto match;
  1335. X    } else
  1336. X    if (flag & KILL_ON_REGEXP) {
  1337. X        if (regexec_fold(kl->kill_regexp, string)) goto match;
  1338. X    } else
  1339. X        if (strmatch_fold(kl->kill_pattern, string)) goto match;
  1340. X
  1341. X     failed:
  1342. X    if ((flag & AND_MATCH) == 0) continue;
  1343. X
  1344. X    do            /* skip next */
  1345. X        kl = kl->next_kill;
  1346. X    while (kl && (kl->kill_flag & AND_MATCH));
  1347. X    if (kl) continue;
  1348. X    break;
  1349. X
  1350. X     match:
  1351. X    if (flag & AND_MATCH) continue;
  1352. X    break;
  1353. X    }
  1354. X    return kl;
  1355. X}
  1356. X
  1357. X
  1358. Xkill_article(ah)
  1359. Xarticle_header *ah;
  1360. X{
  1361. X    register kill_list_entry *kl;
  1362. X    int unless_match = 0;
  1363. X
  1364. X    kl = exec_kill((kill_list_entry *)(current_group->kill_list), ah,
  1365. X           &unless_match, 0, 0);
  1366. X    if (kl == NULL && group_regexp_table != NULL) {
  1367. X    if (current_kill_group != current_group) build_regexp_kill();
  1368. X    kl = exec_kill(regexp_kill_list, ah, &unless_match, 0, 0);
  1369. X    }
  1370. X    if (kl == NULL)
  1371. X    kl = exec_kill(global_kill_list, ah, &unless_match, 0, 0);
  1372. X
  1373. X    if (kl != NULL) {
  1374. X    if (kl->kill_flag & AUTO_KILL) {
  1375. X        killed_articles++;
  1376. X        return 1;
  1377. X    }
  1378. X
  1379. X    if (kl->kill_flag & AUTO_SELECT)
  1380. X        ah->attr = A_AUTO_SELECT;
  1381. X    return 0;
  1382. X    }
  1383. X
  1384. X    if (unless_match) {
  1385. X    killed_articles++;
  1386. X    return 1;
  1387. X    }
  1388. X
  1389. X    return 0;
  1390. X}
  1391. X
  1392. X
  1393. Xauto_select_article(ah, do_select)
  1394. Xarticle_header *ah;
  1395. Xint do_select;
  1396. X{
  1397. X    register kill_list_entry *kl;
  1398. X    int dummy;
  1399. X
  1400. X    if (do_select == 1) {
  1401. X    kl = ah->a_group ? (kill_list_entry *)(ah->a_group->kill_list) :
  1402. X        (kill_list_entry *)(current_group->kill_list);
  1403. X    kl = exec_kill(kl, ah, &dummy, !do_select, do_select);
  1404. X    if (kl == NULL && group_regexp_table != NULL) {
  1405. X        if (current_kill_group != current_group) build_regexp_kill();
  1406. X        kl = exec_kill(regexp_kill_list, ah, &dummy, !do_select, do_select);
  1407. X    }
  1408. X    if (kl == NULL)
  1409. X        kl = exec_kill(global_kill_list, ah, &dummy, !do_select, do_select);
  1410. X    } else {
  1411. X    kl = exec_kill(&latest_kl_entry, ah, &dummy, !do_select, do_select);
  1412. X    }
  1413. X
  1414. X    if (kl == NULL) return 0;
  1415. X
  1416. X    if (!do_select) killed_articles++;
  1417. X    return 1;
  1418. X}
  1419. X
  1420. X
  1421. Xstatic fput_pattern(p, f)
  1422. Xregister char *p;
  1423. Xregister FILE *f;
  1424. X{
  1425. X    register char c;
  1426. X
  1427. X    while (c = *p++) {
  1428. X    if (c == ':' || c == '\\') putc('\\', f);
  1429. X    putc(c, f);
  1430. X    }
  1431. X}
  1432. X
  1433. Xstatic char *get_pattern(p, lenp, more)
  1434. Xregister char *p;
  1435. Xint *lenp, more;
  1436. X{
  1437. X    register char c, *q, *start;
  1438. X
  1439. X    start = q = p;
  1440. X    while (c = *p++) {
  1441. X    if (c == '\\') {
  1442. X        c = *p++;
  1443. X        if (c != ':' && c != '\\') *q++ = '\\';
  1444. X        *q++ = c;
  1445. X        continue;
  1446. X    }
  1447. X    if (more) {
  1448. X        if (c == ':') break;
  1449. X        if (c == NL) return NULL;
  1450. X    } else
  1451. X        if (c == NL) break;
  1452. X
  1453. X    *q++ = c;
  1454. X    }
  1455. X
  1456. X    if (c == NUL) return NULL;
  1457. X
  1458. X    *q++ = NUL;
  1459. X    *lenp = q - start;
  1460. X    return p;
  1461. X}
  1462. X
  1463. Xenter_kill_file(gh, pattern, flag, days)
  1464. Xgroup_header *gh;
  1465. Xchar *pattern;
  1466. Xregister flag_type flag;
  1467. Xint days;
  1468. X{
  1469. X    FILE *killf;
  1470. X    register kill_list_entry *kl;
  1471. X    regexp *re;
  1472. X    char *str;
  1473. X
  1474. X    str = copy_str(pattern);
  1475. X
  1476. X    if ((flag & KILL_CASE_MATCH) == 0)
  1477. X    fold_string(str);
  1478. X
  1479. X    if (flag & KILL_ON_REGEXP) {
  1480. X    re = regcomp(pattern);
  1481. X    if (re == NULL) return;
  1482. X    } else
  1483. X    re = NULL;
  1484. X
  1485. X    killf = open_file(relative(nn_directory, "kill"), OPEN_APPEND);
  1486. X    if (killf == NULL) {
  1487. X    msg("cannot create kill file");
  1488. X    return;
  1489. X    }
  1490. X
  1491. X    if (days >= 0) {
  1492. X    if (days == 0) days = 30;
  1493. X    fprintf(killf, "%lu:", (long)(cur_time() + days DAYS));
  1494. X    }
  1495. X
  1496. X    if (gh) fputs(gh->group_name, killf);
  1497. X    fputc(':', killf);
  1498. X
  1499. X    if (flag & KILL_UNLESS_MATCH) fputc(EXT_KILL_UNLESS_MATCH, killf);
  1500. X    if (flag & AUTO_KILL) fputc(EXT_AUTO_KILL, killf);
  1501. X    if (flag & AUTO_SELECT) fputc(EXT_AUTO_SELECT, killf);
  1502. X    if (flag & ON_FOLLOW_UP) fputc(EXT_ON_FOLLOW_UP, killf);
  1503. X    if (flag & ON_ANY_REFERENCES) fputc(EXT_ON_ANY_REFERENCES, killf);
  1504. X    if (flag & ON_SENDER) fputc(EXT_ON_SENDER, killf);
  1505. X    if (flag & ON_SUBJECT) fputc(EXT_ON_SUBJECT, killf);
  1506. X    if (flag & KILL_CASE_MATCH) fputc(EXT_KILL_CASE_MATCH, killf);
  1507. X    if (flag & KILL_ON_REGEXP) fputc(EXT_KILL_ON_REGEXP, killf);
  1508. X    fputc(':', killf);
  1509. X
  1510. X    fput_pattern(pattern, killf);
  1511. X    fputc(NL, killf);
  1512. X
  1513. X    fclose(killf);
  1514. X    rm_kill_file();
  1515. X
  1516. X    kl = newobj(kill_list_entry, 1);
  1517. X
  1518. X    latest_kl_entry.kill_pattern = kl->kill_pattern = str;
  1519. X    latest_kl_entry.kill_regexp = kl->kill_regexp = re;
  1520. X    latest_kl_entry.kill_flag = kl->kill_flag = flag;
  1521. X    latest_kl_entry.next_kill = NULL;
  1522. X
  1523. X    if (gh) {
  1524. X    kl->next_kill = (kill_list_entry *)(gh->kill_list);
  1525. X    gh->kill_list = (char *)kl;
  1526. X    } else {
  1527. X    kl->next_kill = global_kill_list;
  1528. X    global_kill_list = kl;
  1529. X    }
  1530. X}
  1531. X
  1532. X
  1533. Xtypedef struct {
  1534. X    group_number    ck_group;
  1535. X    flag_type        ck_flag;
  1536. X    long        ck_pattern_index;
  1537. X} comp_kill_entry;
  1538. X
  1539. Xtypedef struct {
  1540. X    long        ckh_magic;
  1541. X    time_t        ckh_db_check;
  1542. X    off_t        ckh_pattern_offset;
  1543. X    long        ckh_pattern_size;
  1544. X    long        ckh_entries;
  1545. X    long        ckh_regexp_size;
  1546. X} comp_kill_header;
  1547. X
  1548. X
  1549. Xkill_menu(ah)
  1550. Xarticle_header *ah;
  1551. X{
  1552. X    int days;
  1553. X    register flag_type flag;
  1554. X    char *mode1, *mode2;
  1555. X    char *pattern, *dflt, *days_str, buffer[512];
  1556. X    extern article_header *get_menu_article();
  1557. X    group_header *gh;
  1558. X
  1559. X    prompt("\1AUTO\1 (k)ill or (s)elect (CR => Kill subject 1 month) ");
  1560. X    switch (get_c()) {
  1561. X     case CR:
  1562. X     case NL:
  1563. X    if (ah == NULL) {
  1564. X        ah = get_menu_article();
  1565. X        if (ah == NULL) return -1;
  1566. X    }
  1567. X
  1568. X    strcpy(buffer, ah->subject);
  1569. X    enter_kill_file(current_group, buffer,
  1570. X            AUTO_KILL | ON_SUBJECT | KILL_CASE_MATCH, 30);
  1571. X    msg("DONE");
  1572. X    return 1;
  1573. X
  1574. X     case 'k':
  1575. X     case 'K':
  1576. X     case '!':
  1577. X    flag = AUTO_KILL;
  1578. X    mode1 = "KILL";
  1579. X    break;
  1580. X     case 's':
  1581. X     case 'S':
  1582. X     case '+':
  1583. X    flag = AUTO_SELECT;
  1584. X    mode1 = "SELECT";
  1585. X    break;
  1586. X     default:
  1587. X    return -1;
  1588. X    }
  1589. X
  1590. X    prompt("\1AUTO %s\1 on (s)ubject or (n)ame  (s)", mode1);
  1591. X
  1592. X    dflt = NULL;
  1593. X    switch (get_c()) {
  1594. X     case 'n':
  1595. X     case 'N':
  1596. X    flag |= ON_SENDER;
  1597. X    if (ah) dflt = ah->sender;
  1598. X    mode2 = "Name";
  1599. X    break;
  1600. X     case 's':
  1601. X     case 'S':
  1602. X     case SP:
  1603. X     case CR:
  1604. X     case NL:
  1605. X    flag |= ON_SUBJECT;
  1606. X    if (ah) dflt = ah->subject;
  1607. X    mode2 = "Subject";
  1608. X    break;
  1609. X     default:
  1610. X    return -1;
  1611. X    }
  1612. X
  1613. X    prompt("\1%s %s:\1 (%=/) ", mode1, mode2);
  1614. X
  1615. X    pattern = get_s(dflt, NONE, "%=/", NULL_FCT);
  1616. X    if (pattern == NULL) return -1;
  1617. X    if (*pattern == NUL || *pattern == '%' || *pattern == '=') {
  1618. X    if (dflt && *dflt)
  1619. X        pattern = dflt;
  1620. X    else {
  1621. X        if ((ah = get_menu_article()) == NULL) return -1;
  1622. X        pattern = (flag & ON_SUBJECT) ? ah->subject : ah->sender;
  1623. X    }
  1624. X    flag |= KILL_CASE_MATCH;
  1625. X    } else
  1626. X    if (*pattern == '/') {
  1627. X        prompt("\1%s %s\1 (regexp): ", mode1, mode2);
  1628. X
  1629. X        pattern = get_s(NONE, NONE, NONE, NULL_FCT);
  1630. X        if (pattern == NULL || *pattern == NUL) return -1;
  1631. X        flag |= KILL_ON_REGEXP;
  1632. X    }
  1633. X
  1634. X    strcpy(buffer, pattern);
  1635. X    pattern = buffer;
  1636. X
  1637. X    prompt("\1%s\1 in (g)roup '%s' or in (a)ll groups  (g)",
  1638. X       mode1, current_group->group_name);
  1639. X
  1640. X    switch (get_c()) {
  1641. X      case 'g':
  1642. X      case 'G':
  1643. X      case SP:
  1644. X      case CR:
  1645. X      case NL:
  1646. X     gh = current_group;
  1647. X     break;
  1648. X      case 'A':
  1649. X      case 'a':
  1650. X     gh = NULL;
  1651. X     break;
  1652. X      default:
  1653. X     return -1;
  1654. X     }
  1655. X
  1656. X    prompt("\1Lifetime of entry in days\1 (p)ermanent  (30) ");
  1657. X    days_str = get_s(" 30 days", NONE, "pP", NULL_FCT);
  1658. X    if (days_str == NULL) return -1;
  1659. X
  1660. X    if (*days_str == NUL) {
  1661. X        days_str = "30 days";
  1662. X    days = 30;
  1663. X    } else if (*days_str == 'p' || *days_str == 'P') {
  1664. X    days_str = "perm";
  1665. X    days = -1;
  1666. X    } else if (isdigit(*days_str)) {
  1667. X    days = atoi(days_str);
  1668. X    sprintf(days_str, "%d days", days);
  1669. X    } else {
  1670. X    ding();
  1671. X    return -1;
  1672. X    }
  1673. X
  1674. X    prompt("\1CONFIRM\1 %s %s %s%s: %-.35s%s ",
  1675. X       mode1, mode2, days_str,
  1676. X       (flag & KILL_CASE_MATCH) ? " exact" :
  1677. X       (flag & KILL_ON_REGEXP) ? " regexp" : "",
  1678. X       pattern, strlen(pattern) > 35 ? "..." : "");
  1679. X    if (yes(0) <= 0) return -1;
  1680. X
  1681. X    enter_kill_file(gh, pattern, flag, days);
  1682. X
  1683. X    return (flag & AUTO_KILL) ? 1 : 0;
  1684. X}
  1685. X
  1686. Xstatic compile_kill_file()
  1687. X{
  1688. X    FILE *killf, *compf, *patternf, *dropf;
  1689. X    comp_kill_header header;
  1690. X    comp_kill_entry  entry;
  1691. X    time_t now, age;
  1692. X    off_t cur_line_start;
  1693. X    char line[512];
  1694. X    register char *cp, *np;
  1695. X    register int c;
  1696. X    group_header *gh;
  1697. X    flag_type flag, fields[10];
  1698. X    extern char *temp_file;
  1699. X    int any_errors, nfield, nf, len;
  1700. X
  1701. X    any_errors = 0;
  1702. X    header.ckh_entries = header.ckh_regexp_size = 0;
  1703. X
  1704. X    killf = open_file(relative(nn_directory, KILL_FILE),
  1705. X              OPEN_READ | DONT_CREATE);
  1706. X    if (killf == NULL) return 0;
  1707. X
  1708. X    compf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_CREATE);
  1709. X    if (compf == NULL) goto err1;
  1710. X
  1711. X    new_temp_file();
  1712. X    if ((patternf = open_file(temp_file, OPEN_CREATE)) == NULL)
  1713. X    goto err2;
  1714. X
  1715. X    dropf = NULL;
  1716. X
  1717. X    printf("\nCompiling kill file\n");
  1718. X
  1719. X    fseek(compf, (off_t)sizeof(header), 0);
  1720. X
  1721. X    now = cur_time();
  1722. X
  1723. X next_entry:
  1724. X
  1725. X    for (;;) {
  1726. X    cur_line_start = ftell(killf);
  1727. X
  1728. X    if (fgets(line, 512, killf) == NULL) break;
  1729. X
  1730. X    cp = line;
  1731. X    while (*cp && isascii(*cp) && isspace(*cp)) cp++;
  1732. X    if (*cp == NUL || *cp == '#' || !isascii(*cp)) continue;
  1733. X
  1734. X    if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
  1735. X
  1736. X    /* optional "age:" */
  1737. X
  1738. X    if (np != cp && isdigit(*cp)) {
  1739. X        *np++ = NUL;
  1740. X        age = (time_t)atol(cp);
  1741. X        if (age < now) goto drop_entry;
  1742. X        cp = np;
  1743. X        if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
  1744. X    }
  1745. X
  1746. X    /* "group-name:"  or "/regexp:" or ":" for all groups */
  1747. X
  1748. X    flag = COMP_KILL_ENTRY;
  1749. X
  1750. X    if (np == cp) {
  1751. X        entry.ck_group = -1;
  1752. X        np++;
  1753. X    } else {
  1754. X        *np++ = NUL;
  1755. X        if (*cp == '/') {
  1756. X        entry.ck_group = (long)ftell(patternf);
  1757. X        cp++;
  1758. X        len = strlen(cp) + 1;
  1759. X        if (fwrite(cp, sizeof(char), len, patternf) != len)
  1760. X            goto err3;
  1761. X        flag |= GROUP_REGEXP | GROUP_REGEXP_HDR ;
  1762. X        header.ckh_regexp_size++;
  1763. X        } else {
  1764. X        if ((gh = lookup(cp)) == NULL) {
  1765. X            printf("Unknown group in kill file: %s\n", cp);
  1766. X            any_errors++;
  1767. X            goto drop_entry;
  1768. X        }
  1769. X        entry.ck_group = gh->group_num;
  1770. X        }
  1771. X    }
  1772. X
  1773. X    /* flags */
  1774. X
  1775. X    cp = np;
  1776. X    nfield = 0;
  1777. X
  1778. X    for (;;) {
  1779. X        switch (*cp++) {
  1780. X         case EXT_AND_MATCH:
  1781. X         case EXT_OR_MATCH:
  1782. X        fields[nfield++] = flag;
  1783. X        flag &= ~(AND_MATCH | ON_SUBJECT | ON_SENDER |
  1784. X              KILL_CASE_MATCH | KILL_ON_REGEXP |
  1785. X              GROUP_REGEXP_HDR);
  1786. X        flag |= (cp[-1] == EXT_AND_MATCH) ? AND_MATCH : OR_MATCH;
  1787. X        continue;
  1788. X         case EXT_AUTO_KILL:
  1789. X        flag |= AUTO_KILL;
  1790. X        continue;
  1791. X         case EXT_AUTO_SELECT:
  1792. X        flag |= AUTO_SELECT;
  1793. X        continue;
  1794. X         case EXT_ON_FOLLOW_UP:
  1795. X        flag |= ON_FOLLOW_UP;
  1796. X        continue;
  1797. X         case EXT_ON_ANY_REFERENCES:
  1798. X        flag |= ON_ANY_REFERENCES;
  1799. X        continue;
  1800. X         case EXT_ON_SUBJECT:
  1801. X        flag |= ON_SUBJECT;
  1802. X        continue;
  1803. X         case EXT_ON_SENDER:
  1804. X        flag |= ON_SENDER;
  1805. X        continue;
  1806. X         case EXT_KILL_CASE_MATCH:
  1807. X        flag |= KILL_CASE_MATCH;
  1808. X        continue;
  1809. X         case EXT_KILL_UNLESS_MATCH:
  1810. X        flag |= KILL_UNLESS_MATCH;
  1811. X        continue;
  1812. X         case EXT_KILL_ON_REGEXP:
  1813. X        flag |= KILL_ON_REGEXP;
  1814. X        continue;
  1815. X         case ':':
  1816. X        break;
  1817. X         case NL:
  1818. X        goto bad_entry;
  1819. X         default:
  1820. X        printf("Ignored flag '%c' in kill file\n", cp[-1]);
  1821. X        any_errors++;
  1822. X        continue;
  1823. X        }
  1824. X        break;
  1825. X    }
  1826. X
  1827. X    fields[nfield++] = flag;
  1828. X
  1829. X    for (nf = 0; --nfield >= 0; nf++) {
  1830. X        entry.ck_flag = flag = fields[nf];
  1831. X        np = cp;
  1832. X        if ((cp = get_pattern(np, &len, nfield)) == NULL) goto bad_entry;
  1833. X
  1834. X        if ((flag & KILL_CASE_MATCH) == 0)
  1835. X        fold_string(np);
  1836. X
  1837. X        entry.ck_pattern_index = ftell(patternf);
  1838. X
  1839. X        if (fwrite((char *)&entry, sizeof(entry), 1, compf) != 1)
  1840. X        goto err3;
  1841. X
  1842. X        if (fwrite(np, sizeof(char), len, patternf) != len)
  1843. X        goto err3;
  1844. X
  1845. X        header.ckh_entries++;
  1846. X    }
  1847. X    }
  1848. X
  1849. X    header.ckh_pattern_size = ftell(patternf);
  1850. X
  1851. X    fclose(patternf);
  1852. X    patternf = open_file(temp_file, OPEN_READ | OPEN_UNLINK);
  1853. X    if (patternf == NULL) goto err2;
  1854. X
  1855. X    header.ckh_pattern_offset = ftell(compf);
  1856. X
  1857. X    while ((c = getc(patternf)) != EOF)
  1858. X    putc(c, compf);
  1859. X
  1860. X    fclose(patternf);
  1861. X
  1862. X    rewind(compf);
  1863. X
  1864. X    header.ckh_magic = COMP_KILL_MAGIC;
  1865. X    header.ckh_db_check = master.db_created;
  1866. X
  1867. X    if (fwrite((char *)&header, sizeof(header), 1, compf) != 1)
  1868. X    goto err2;
  1869. X
  1870. X    fclose(compf);
  1871. X    fclose(killf);
  1872. X    if (dropf != NULL) fclose(dropf);
  1873. X
  1874. X    if (any_errors) {
  1875. X    putchar(NL);
  1876. X    any_key(0);
  1877. X    }
  1878. X
  1879. X    return 1;
  1880. X
  1881. X bad_entry:
  1882. X    printf("Incomplete kill file entry:\n%s", line);
  1883. X    fl;
  1884. X    any_errors++;
  1885. X
  1886. X drop_entry:
  1887. X    if (dropf == NULL) {
  1888. X    dropf = open_file(relative(nn_directory, KILL_FILE),
  1889. X              OPEN_UPDATE | DONT_CREATE);
  1890. X    if (dropf == NULL) goto next_entry;
  1891. X    }
  1892. X    fseek(dropf, cur_line_start, 0);
  1893. X    fwrite("# ", sizeof(char), 2, dropf);
  1894. X    goto next_entry;
  1895. X
  1896. X err3:
  1897. X    fclose(patternf);
  1898. X    unlink(temp_file);
  1899. X err2:
  1900. X    fclose(compf);
  1901. X    rm_kill_file();
  1902. X err1:
  1903. X    fclose(killf);
  1904. X    if (dropf != NULL) fclose(dropf);
  1905. X
  1906. X    msg("cannot compile kill file");
  1907. X    return 0;
  1908. X}
  1909. X
  1910. Xinit_kill()
  1911. X{
  1912. X    FILE *killf;
  1913. X    comp_kill_header header;
  1914. X    comp_kill_entry  entry;
  1915. X    register kill_list_entry *kl;
  1916. X    register kill_group_regexp *tb;
  1917. X    register group_header *gh;
  1918. X    time_t kill_age, comp_age;
  1919. X    register long n;
  1920. X    int first_try = 1;
  1921. X    import char delayed_msg[];
  1922. X
  1923. X    Loop_Groups_Header(gh)
  1924. X    gh->kill_list = NULL;
  1925. X
  1926. X    kill_age = file_exist(relative(nn_directory, KILL_FILE), "frw");
  1927. X    if (kill_age == 0) return 0;
  1928. X
  1929. X    comp_age = file_exist(relative(nn_directory, COMPILED_KILL), "fr");
  1930. X again:
  1931. X    if (comp_age < kill_age && !compile_kill_file()) return 0;
  1932. X
  1933. X    kill_tab = NULL;
  1934. X    kill_patterns = NULL;
  1935. X    group_regexp_table = NULL;
  1936. X    regexp_table_size = 0;
  1937. X
  1938. X    killf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_READ);
  1939. X    if (killf == NULL) return 0;
  1940. X
  1941. X    if (fread((char *)&header, sizeof(header), 1, killf) != 1) goto err;
  1942. X    /* MAGIC check: format changed or using different hardware */
  1943. X    if (header.ckh_magic != COMP_KILL_MAGIC) goto err;
  1944. X    /* DB check: if database is rebuilt, group numbers may change */
  1945. X    if (header.ckh_db_check != master.db_created) goto err;
  1946. X
  1947. X    kill_patterns = newstr(header.ckh_pattern_size);
  1948. X    kill_tab = newobj(kill_list_entry, header.ckh_entries);
  1949. X    if (regexp_table_size = header.ckh_regexp_size)
  1950. X    group_regexp_table = newobj(kill_group_regexp, header.ckh_regexp_size);
  1951. X
  1952. X    fseek(killf, (off_t)(header.ckh_entries * sizeof(entry)), 1);
  1953. X    if (fread(kill_patterns, sizeof(char), (int)header.ckh_pattern_size, killf)
  1954. X    !=  header.ckh_pattern_size) goto err;
  1955. X
  1956. X    tb = group_regexp_table;
  1957. X
  1958. X    fseek(killf, (off_t)sizeof(header), 0);
  1959. X    for (n = header.ckh_entries, kl = kill_tab; --n >= 0; kl++) {
  1960. X    if (fread((char *)&entry, sizeof(entry), 1, killf) != 1) goto err;
  1961. X    if (header.ckh_pattern_size <= entry.ck_pattern_index ||
  1962. X        entry.ck_pattern_index < 0) goto err;
  1963. X
  1964. X    kl->kill_pattern = kill_patterns + entry.ck_pattern_index;
  1965. X    kl->kill_flag = entry.ck_flag;
  1966. X
  1967. X    if (kl->kill_flag & KILL_ON_REGEXP)
  1968. X        kl->kill_regexp = regcomp(kl->kill_pattern);
  1969. X    else
  1970. X        kl->kill_regexp = NULL;
  1971. X
  1972. X    if (kl->kill_flag & GROUP_REGEXP) {
  1973. X        if (kl->kill_flag & GROUP_REGEXP_HDR) {
  1974. X        if (header.ckh_pattern_size <= entry.ck_group ||
  1975. X            entry.ck_group < 0) goto err;
  1976. X        tb->group_regexp = regcomp(kill_patterns + entry.ck_group);
  1977. X        } else
  1978. X        tb->group_regexp = NULL;
  1979. X        tb->kill_entry = kl;
  1980. X        tb++;
  1981. X    } else
  1982. X    if (entry.ck_group >= 0) {
  1983. X        gh = active_groups + entry.ck_group;
  1984. X        kl->next_kill = (kill_list_entry *)(gh->kill_list);
  1985. X        gh->kill_list = (char *)kl;
  1986. X    } else {
  1987. X        kl->next_kill = global_kill_list;
  1988. X        global_kill_list = kl;
  1989. X    }
  1990. X    }
  1991. X
  1992. X    fclose(killf);
  1993. X
  1994. X    return 1;
  1995. X
  1996. X err:
  1997. X    if (group_regexp_table != NULL) freeobj(group_regexp_table);
  1998. X    if (kill_patterns != NULL) freeobj(kill_patterns);
  1999. X    if (kill_tab != NULL) freeobj(kill_tab);
  2000. X
  2001. X    fclose(killf);
  2002. X    rm_kill_file();
  2003. X    if (first_try) {
  2004. X    first_try = 0;
  2005. X    comp_age = 0;
  2006. X    goto again;
  2007. X    }
  2008. X
  2009. X    strcpy(delayed_msg, "Error in compiled kill file (ignored)");
  2010. X
  2011. X    Loop_Groups_Header(gh)
  2012. X    gh->kill_list = NULL;
  2013. X
  2014. X    global_kill_list = NULL;
  2015. X    group_regexp_table = NULL;
  2016. X
  2017. X    return 0;
  2018. X}
  2019. X
  2020. X
  2021. X
  2022. Xrm_kill_file()
  2023. X{
  2024. X    unlink(relative(nn_directory, COMPILED_KILL));
  2025. X}
  2026. X
  2027. X
  2028. Xstatic free_kill_list(kl)
  2029. Xregister kill_list_entry *kl;
  2030. X{
  2031. X    register kill_list_entry *nxt;
  2032. X    while (kl) {
  2033. X    nxt = kl->next_kill;
  2034. X    if (kl->kill_regexp != NULL) freeobj(kl->kill_regexp);
  2035. X    if ((kl->kill_flag & COMP_KILL_ENTRY) == 0) {
  2036. X        if (kl->kill_pattern != NULL) freeobj(kl->kill_pattern);
  2037. X        freeobj(kl);
  2038. X    }
  2039. X    kl = nxt;
  2040. X    }
  2041. X}
  2042. X
  2043. Xfree_kill_entries()
  2044. X{
  2045. X    register group_header *gh;
  2046. X    register kill_group_regexp *tb;
  2047. X    register int n;
  2048. X
  2049. X    Loop_Groups_Header(gh)
  2050. X    if (gh->kill_list) {
  2051. X        free_kill_list((kill_list_entry *)(gh->kill_list));
  2052. X        gh->kill_list = NULL;
  2053. X    }
  2054. X
  2055. X    free_kill_list(global_kill_list);
  2056. X    global_kill_list = NULL;
  2057. X
  2058. X    if (tb = group_regexp_table) {
  2059. X    for (n = regexp_table_size; --n >= 0; tb++)
  2060. X        if (tb->group_regexp != NULL) freeobj(tb->group_regexp);
  2061. X
  2062. X    freeobj(group_regexp_table);
  2063. X    group_regexp_table = NULL;
  2064. X    }
  2065. X
  2066. X    if (kill_patterns != NULL) freeobj(kill_patterns);
  2067. X    if (kill_tab != NULL) freeobj(kill_tab);
  2068. X}
  2069. X
  2070. X
  2071. Xstatic flag_type pk_prev_and;
  2072. X
  2073. Xstatic print_kill(kl)
  2074. Xregister kill_list_entry *kl;
  2075. X{
  2076. X    register flag_type flag = kl->kill_flag;
  2077. X
  2078. X    if (pg_next() < 0) return -1;
  2079. X
  2080. X    if (pk_prev_and)
  2081. X    printf("\r    AND ");
  2082. X    else
  2083. X    printf("\r%s%s ON ",
  2084. X           flag & AUTO_KILL ? "AUTO KILL" :
  2085. X           flag & AUTO_SELECT ? "AUTO SELECT" : "",
  2086. X
  2087. X           (flag & KILL_UNLESS_MATCH) == 0 ? "" :
  2088. X           flag & AUTO_KILL ? " UNLESS" :
  2089. X           flag & AUTO_SELECT ? "" : "KEEP");
  2090. X
  2091. X    printf("%s '%.35s'%s\n",
  2092. X       flag & ON_SUBJECT ? "SUBJECT" :
  2093. X       flag & ON_SENDER ? "NAME" : "????",
  2094. X
  2095. X       kl->kill_pattern,
  2096. X
  2097. X       flag & KILL_CASE_MATCH ?
  2098. X       (flag & KILL_ON_REGEXP ? " (re exact)" : " (exact)") :
  2099. X       (flag & KILL_ON_REGEXP ? " (re fold)" : ""));
  2100. X
  2101. X    pk_prev_and = flag & AND_MATCH;
  2102. X
  2103. X    return 0;
  2104. X}
  2105. X
  2106. Xdump_kill_list()
  2107. X{
  2108. X    register kill_list_entry *kl;
  2109. X
  2110. X    pg_init(0, 1);
  2111. X    pg_next();
  2112. X
  2113. X    kl = (kill_list_entry *)(current_group->kill_list);
  2114. X    if (current_kill_group != current_group) build_regexp_kill();
  2115. X
  2116. X    if (kl == NULL && regexp_kill_list == NULL) {
  2117. X    printf("No kill entries for %s", current_group->group_name);
  2118. X    } else {
  2119. X    so_printf("\1GROUP %s kill list entries\1", current_group->group_name);
  2120. X
  2121. X    pk_prev_and = 0;
  2122. X    for ( ; kl; kl = kl->next_kill)
  2123. X        if (print_kill(kl) < 0) goto out;
  2124. X
  2125. X    pk_prev_and = 0;
  2126. X    for (kl = regexp_kill_list ; kl; kl = kl->next_kill)
  2127. X        if (print_kill(kl) < 0) goto out;
  2128. X
  2129. X    if (pg_next() < 0) goto out;
  2130. X    }
  2131. X
  2132. X    if (pg_next() < 0) goto out;
  2133. X    so_printf("\1GLOBAL kill list entries:\1");
  2134. X
  2135. X    pk_prev_and = 0;
  2136. X    for (kl = global_kill_list; kl != NULL; kl = kl->next_kill)
  2137. X    if (print_kill(kl) < 0) goto out;
  2138. X
  2139. X out:
  2140. X    pg_end();
  2141. X}
  2142. END_OF_FILE
  2143.   if test 21836 -ne `wc -c <'kill.c'`; then
  2144.     echo shar: \"'kill.c'\" unpacked with wrong size!
  2145.   fi
  2146.   # end of 'kill.c'
  2147. fi
  2148. if test -f 'sort.c' -a "${1}" != "-c" ; then 
  2149.   echo shar: Will not clobber existing file \"'sort.c'\"
  2150. else
  2151.   echo shar: Extracting \"'sort.c'\" \(7546 characters\)
  2152.   sed "s/^X//" >'sort.c' <<'END_OF_FILE'
  2153. X/*
  2154. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  2155. X *
  2156. X *    Article sorting.
  2157. X */
  2158. X
  2159. X#include "config.h"
  2160. X#include "articles.h"
  2161. X
  2162. X
  2163. X
  2164. Xexport int subject_match_limit = 20;     /* "strncmp" limit for subjects */
  2165. Xexport int match_skip_prefix = 0; /* skip first N bytes in matches */
  2166. Xexport int match_parts_equal = 0; /* match digits as equal */
  2167. X
  2168. Xexport int sort_mode = 1;        /* default sort mode */
  2169. X
  2170. X/*
  2171. X *    String matching macroes.
  2172. X *
  2173. X *     MATCH_DROP(t, a) and MATCH_DROP(t, b) must both be proven false
  2174. X *    before MATCH_???(t, a, b) is used.
  2175. X */
  2176. X
  2177. X#define    MATCH_DROP(table, c) ( c & 0200 || table[c] == 0 )
  2178. X#define MATCH_EQ(table, a, b) ( a == b || table[a] == table[b] )
  2179. X#define MATCH_LS_EQ(table, a, b) ( a <= b || table[a] <= table[b] )
  2180. X#define MATCH_LS(table, a, b) ( a < b || table[a] < table[b] )
  2181. X#define    MATCH_CMP(table, a, b) (table[a] - table[b])
  2182. X
  2183. Xstatic char match_subject[128] = {
  2184. X
  2185. X/*  NUL SOH STX ETX EOT ENQ ACK BEL BS  TAB NL  VT  FF  CR  SO  SI  */
  2186. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
  2187. X
  2188. X/*  DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM  SUB ESC FS  GS  RS  US  */
  2189. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
  2190. X
  2191. X/*  SP  !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
  2192. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 99, 00, 00, 00, 00,
  2193. X/*                                              ^^                  */
  2194. X
  2195. X/*  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
  2196. X     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 00, 00, 00, 00, 00, 00,
  2197. X
  2198. X/*  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
  2199. X    00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  2200. X
  2201. X/*  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
  2202. X    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00,
  2203. X
  2204. X/*  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
  2205. X    00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  2206. X
  2207. X/*  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~   DEL */
  2208. X    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00
  2209. X};
  2210. X
  2211. X
  2212. Xstatic order_subj_date(ah1, ah2)
  2213. Xarticle_header **ah1, **ah2;
  2214. X{
  2215. X    register char *a = (**ah1).subject, *b = (**ah2).subject;
  2216. X    register char ca, cb;
  2217. X    register int p, len;
  2218. X
  2219. X    if (match_skip_prefix) {
  2220. X    if ((**ah1).subj_length > match_skip_prefix) {
  2221. X        if ((**ah2).subj_length > match_skip_prefix) {
  2222. X        a += match_skip_prefix;
  2223. X        b += match_skip_prefix;
  2224. X        } else
  2225. X        return 1;
  2226. X    } else {
  2227. X        if ((**ah2).subj_length > match_skip_prefix) {
  2228. X        return -1;
  2229. X        }
  2230. X    }
  2231. X    }
  2232. X
  2233. X    for (len = 0; ; len++, a++, b++) {
  2234. X    while ((ca = *a) && MATCH_DROP(match_subject, ca)) a++;
  2235. X    while ((cb = *b) && MATCH_DROP(match_subject, cb)) b++;
  2236. X
  2237. X    if (ca == NUL) {
  2238. X        if (cb == NUL) break;
  2239. X        if (len >= subject_match_limit) break;
  2240. X        return -1;
  2241. X    }
  2242. X
  2243. X    if (cb == NUL) {
  2244. X        if (len >= subject_match_limit) break;
  2245. X        return 1;
  2246. X    }
  2247. X
  2248. X    if (p = MATCH_CMP(match_subject, ca, cb)) return p;
  2249. X    }
  2250. X
  2251. X    if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
  2252. X    if ((**ah1).t_stamp < (**ah2).t_stamp) return -1;
  2253. X    return 0;
  2254. X}
  2255. X
  2256. X/* data_subj_date can only be used after root_t_stamp is set */
  2257. X
  2258. Xstatic order_date_subj_date(ah1, ah2)
  2259. Xarticle_header **ah1, **ah2;
  2260. X{
  2261. X    register time_stamp t1 = (**ah1).root_t_stamp;
  2262. X    register time_stamp t2 = (**ah2).root_t_stamp;
  2263. X
  2264. X    if (t1 > t2) return 1;
  2265. X    if (t1 < t2) return -1;
  2266. X    return order_subj_date(ah1, ah2);    /* get original order */
  2267. X}
  2268. X
  2269. X
  2270. Xstatic order_arrival(a, b)
  2271. Xarticle_header **a, **b;
  2272. X{
  2273. X    register long i;
  2274. X
  2275. X    if ((i = (int)((*a)->a_number - (*b)->a_number)) == 0)
  2276. X    i = (*a)->fpos - (*b)->fpos;
  2277. X
  2278. X    return (i > 0) ? 1 : (i < 0) ? -1 : 0;
  2279. X}
  2280. X
  2281. Xstatic order_date(ah1, ah2)
  2282. Xregister article_header **ah1, **ah2;
  2283. X{
  2284. X    if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
  2285. X    if ((**ah1).t_stamp < (**ah2).t_stamp) return -1;
  2286. X    return 0;
  2287. X}
  2288. X
  2289. Xstatic order_from_date(ah1, ah2)
  2290. Xregister article_header **ah1, **ah2;
  2291. X{
  2292. X    register int i;
  2293. X
  2294. X    if (i = strcmp((**ah1).sender, (**ah2).sender)) return i;
  2295. X    return order_date(ah1, ah2);
  2296. X}
  2297. X
  2298. Xstatic flag_type article_equal(ah1, ah2) /* ah1.hdr == ah2.hdr */
  2299. Xarticle_header **ah1, **ah2;
  2300. X{
  2301. X    register char *a = (**ah1).subject, *b = (**ah2).subject;
  2302. X    register int len;
  2303. X
  2304. X    if (match_skip_prefix) {
  2305. X    if ((**ah1).subj_length > match_skip_prefix) {
  2306. X        if ((**ah2).subj_length > match_skip_prefix) {
  2307. X        a += match_skip_prefix;
  2308. X        b += match_skip_prefix;
  2309. X        }
  2310. X    }
  2311. X    }
  2312. X
  2313. X    for (len = 0;; len++, a++, b++) {
  2314. X    while (*a && MATCH_DROP(match_subject, *a)) a++;
  2315. X    while (*b && MATCH_DROP(match_subject, *b)) b++;
  2316. X
  2317. X    if (*a == NUL) {
  2318. X        if (*b == NUL) break;
  2319. X        if (len >= subject_match_limit) return A_ALMOST_SAME;
  2320. X        return 0;
  2321. X    }
  2322. X
  2323. X    if (*b == NUL) {
  2324. X        if (len >= subject_match_limit) return A_ALMOST_SAME;
  2325. X        return 0;
  2326. X    }
  2327. X
  2328. X    if (!MATCH_EQ(match_subject, *a, *b)) {
  2329. X        if (len >= subject_match_limit)
  2330. X        return A_ALMOST_SAME;
  2331. X        if (match_parts_equal && isdigit(*a) && isdigit(*b))
  2332. X        return A_ALMOST_SAME;
  2333. X        return 0;
  2334. X    }
  2335. X    }
  2336. X
  2337. X    return A_SAME;
  2338. X}
  2339. X
  2340. Xsort_articles(mode)
  2341. Xint mode;
  2342. X{
  2343. X    register article_header **app;
  2344. X    register long n;
  2345. X    register flag_type same;
  2346. X    fct_type cmp;
  2347. X
  2348. X    for (n = n_articles; --n >= 0;)
  2349. X    articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  2350. X
  2351. X    if (n_articles <= 1) return;
  2352. X
  2353. X    if (mode == -1) mode = sort_mode;
  2354. X
  2355. X    switch (mode) {
  2356. X     default:
  2357. X     case 0:            /* arrival (no sort) */
  2358. X    cmp = order_arrival;
  2359. X    break;
  2360. X     case 1:            /* date-subject-date */
  2361. X     case 2:            /* subject-date */
  2362. X    cmp = order_subj_date;
  2363. X    break;
  2364. X     case 3:            /* date only */
  2365. X    cmp = order_date;
  2366. X    break;
  2367. X     case 4:            /* sender-date */
  2368. X    cmp = order_from_date;
  2369. X    break;
  2370. X    }
  2371. X
  2372. X    quicksort(articles, n_articles, article_header *, cmp);
  2373. X
  2374. X    articles[0]->root_t_stamp = articles[0]->t_stamp;
  2375. X    for (n = n_articles - 1, app = articles + 1; --n >= 0; app++) {
  2376. X    if (same = article_equal(app, app - 1)) {
  2377. X        app[0]->root_t_stamp = app[-1]->root_t_stamp;
  2378. X        app[0]->flag |= same;
  2379. X        app[-1]->flag |= A_NEXT_SAME;
  2380. X    } else {
  2381. X        app[0]->root_t_stamp = app[0]->t_stamp;
  2382. X    }
  2383. X    }
  2384. X
  2385. X    if (mode == 1)
  2386. X    quicksort(articles, n_articles, article_header *, order_date_subj_date);
  2387. X}
  2388. X
  2389. X
  2390. X/*
  2391. X * Eliminate articles with the A_KILL flag set preserving the present ordering.
  2392. X * This will only release the last entries in the articles array.
  2393. X * Neither strings nor articles headers are released.
  2394. X */
  2395. X
  2396. Xelim_articles(list, list_lgt)
  2397. Xregister article_number *list;
  2398. Xint list_lgt;
  2399. X{
  2400. X    register article_header **srca, **desta;
  2401. X    register article_number n, count;
  2402. X    register flag_type same;
  2403. X    int changed, llen;
  2404. X
  2405. X    count = 0;
  2406. X    changed = 0, llen = 0;
  2407. X    for (n = 0, srca = desta = articles; n < n_articles; n++, srca++) {
  2408. X    if ((*srca)->attr == A_KILL) {
  2409. X        if (list_lgt > 0) {
  2410. X        if (n < *list) {
  2411. X            if (llen) changed = 1;
  2412. X        } else
  2413. X        if (n == *list) {
  2414. X            if (llen) {
  2415. X            llen++;
  2416. X            list_lgt--;
  2417. X            *list++ = -1;
  2418. X            } else
  2419. X            ++(*list);
  2420. X            changed = 1;
  2421. X        }
  2422. X        }
  2423. X        continue;
  2424. X    }
  2425. X    if (list_lgt > 0 && n == *list) {
  2426. X        *list++ = count;
  2427. X        list_lgt--;
  2428. X        llen++;
  2429. X    }
  2430. X    count++;
  2431. X    *desta++ = *srca;
  2432. X    }
  2433. X    if (list_lgt > 0) {
  2434. X    if (!llen) *list = 0;
  2435. X    changed = 1;
  2436. X    }
  2437. X    n_articles = count;
  2438. X
  2439. X    if (changed && n_articles > 0) {
  2440. X    srca = articles;
  2441. X    srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  2442. X    for (n = n_articles - 1, srca++; --n >= 0; srca++) {
  2443. X        srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  2444. X        if (same = article_equal(srca, srca - 1)) {
  2445. X        srca[0]->flag |= same;
  2446. X        srca[-1]->flag |= A_NEXT_SAME;
  2447. X        }
  2448. X    }
  2449. X    }
  2450. X
  2451. X    return changed;
  2452. X}
  2453. END_OF_FILE
  2454.   if test 7546 -ne `wc -c <'sort.c'`; then
  2455.     echo shar: \"'sort.c'\" unpacked with wrong size!
  2456.   fi
  2457.   # end of 'sort.c'
  2458. fi
  2459. echo shar: End of archive 11 \(of 22\).
  2460. cp /dev/null ark11isdone
  2461. MISSING=""
  2462. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
  2463.     if test ! -f ark${I}isdone ; then
  2464.     MISSING="${MISSING} ${I}"
  2465.     fi
  2466. done
  2467. if test "${MISSING}" = "" ; then
  2468.     echo You have unpacked all 22 archives.
  2469.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2470. else
  2471.     echo You still must unpack the following archives:
  2472.     echo "        " ${MISSING}
  2473. fi
  2474. exit 0
  2475.  
  2476. exit 0 # Just in case...
  2477.